package me.zingon.core.context.factory;

import me.zingon.core.context.annotation.SComponent;
import me.zingon.core.context.annotation.SController;
import me.zingon.core.context.annotation.SMapper;
import me.zingon.core.context.annotation.SService;
import me.zingon.core.context.exception.DuplicateBeanException;
import org.yaml.snakeyaml.Yaml;

import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class SReader {

    private static final String SCAN_PACKAGE = "scanPackage";

    private LinkedHashMap config;

    private Map<String, SBeanDefinition> beanDefinitions = new ConcurrentHashMap<>(256);

    public SReader(LinkedHashMap config) {
        this.config=config;
    }

    public Map<String, SBeanDefinition> loadBeanDefinition() {
        File root = new File((String) config.get(SCAN_PACKAGE));
        scanBean((String) ((String) config.get(SCAN_PACKAGE)).replace(".", "/"));
        return beanDefinitions;
    }

    private void scanBean(String packagePath) {
        URL url = this.getClass().getResource("/" + packagePath);
        File rootPath = new File(url.getFile());
        File[] fs = rootPath.listFiles();
        for (File f : fs) {
            if (f.isDirectory()) {
                scanBean(packagePath + "/" + f.getName());
            } else {
                if (!f.getName().endsWith(".class")) {
                    continue;
                }
                String packageName = packagePath.replace("/", ".");
                String fileName = f.getName().replace(".class", "");
                try {
                    Class<?> clazz = Class.forName(packageName + "." + fileName);
                    if (!isComponent(clazz)) {
                        continue;
                    }
                    SBeanDefinition definition;
                    if (!"".equals(getComponentValue(clazz))) {
                        definition = new SBeanDefinition(packageName + "." + fileName, getComponentValue(clazz));
                    } else {
                        definition = new SBeanDefinition(packageName + "." + fileName, toLowerFirstCase(fileName));
                    }
                    if (beanDefinitions.containsKey(definition.getFactoryBeanName()) || beanDefinitions.containsKey(definition.getBeanClassName())) {
                        throw new DuplicateBeanException(definition.getBeanClassName() + " duplicated");
                    }
                    definition.setClazz(clazz);
                    beanDefinitions.put(definition.getBeanClassName(), definition);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (DuplicateBeanException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    private String getComponentValue(Class<?> clazz) {
        if (clazz.isAnnotationPresent(SComponent.class)) {
            return clazz.getAnnotation(SComponent.class).value();
        }
        if (clazz.isAnnotationPresent(SController.class)) {
            return clazz.getAnnotation(SController.class).value();
        }
        if (clazz.isAnnotationPresent(SService.class)) {
            return clazz.getAnnotation(SService.class).value();
        }
        if (clazz.isAnnotationPresent(SMapper.class)) {
            return clazz.getAnnotation(SMapper.class).value();
        }
        return "";
    }

    private boolean isComponent(Class<?> clazz) {
        if (clazz.isAnnotationPresent(SComponent.class)) {
            return true;
        }
        if (clazz.isAnnotationPresent(SController.class)) {
            return true;
        }
        if (clazz.isAnnotationPresent(SService.class)) {
            return true;
        }
        if (clazz.isAnnotationPresent(SMapper.class)) {
            return true;
        }
        return false;
    }

    private String toLowerFirstCase(String simpleName) {
        char[] chars = simpleName.toCharArray();
        if ((chars[0] >= 'A') && (chars[0] <= 'Z')) {
            chars[0] += 32;
        }
        return String.valueOf(chars);
    }
}
